#ifndef __M_SOCKET_H__
#define __M_SOCKET_H__

#include "log.hpp"
#include <string>
#include <cstring>
#include <cerrno>
#include <cstdlib>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <unistd.h>
#include <fcntl.h>

class Socket
{
private:
    int _sock;

public:
    Socket()
    : _sock(-1)
    {}

    Socket(int fd)
    : _sock(fd)
    {}

    int getFd() { return _sock; }

    bool Create()
    {
        _sock = socket(AF_INET, SOCK_STREAM, 0);
        if (_sock == -1)
        {
            LOG(DEG, "create socket failed, %s", strerror(errno));
            return false;
        }
        return true;
    }

    bool Bind(uint16_t port)
    {
        struct sockaddr_in peer;
        peer.sin_family = AF_INET;
        peer.sin_addr.s_addr = INADDR_ANY;
        peer.sin_port = htons(port);
        if (bind(_sock, (sockaddr*)&peer, sizeof peer) == -1)
        {
            LOG(DEG, "bind socket failed");
            return false;
        }
        return true;
    }

    bool Listen(int backlog = 1024)
    {
        if (listen(_sock, backlog) == -1)
        {
            LOG(DEG, "set listen failed, %s", strerror(errno));
            return false;
        }
        return true;
    }

    int Accept()
    {
        int sock = accept(_sock, nullptr, nullptr);
        if (sock == -1)
            LOG(DEG, "accept socket failed");
        return sock;
    }

    bool Connect(const std::string& ip, uint16_t port)
    {
        struct sockaddr_in server;
        server.sin_family = AF_INET;
        server.sin_addr.s_addr = inet_addr(ip.c_str());
        server.sin_port = htons(port);
        if (connect(_sock, (struct sockaddr*)&server, sizeof server) == -1)
        {
            LOG(DEG, "connect server failed");
            return false;
        }
        return true;
    }

    ssize_t Recv(void* buf, size_t len, int flag = 0)
    {
        ssize_t n = recv(_sock, buf, len, flag);
        if (n <= 0)
        {
            if (errno == EAGAIN || errno == EWOULDBLOCK || errno == EINTR)//非阻塞读取且缓冲区内无数据 / 读取时被信号打断
            {
                return 0;
            }
            else//客户端关闭或读取出现错误
            {
                LOG(DEG, "recv failed, %s", strerror(errno));//客户端退出关闭连接也归入到接收错误的情况中
                return -1;
            }
        }
        return n;
    }

    ssize_t Send(const void* buf, size_t len, int flag = 0)//MSG_DONTWAIT
    {
        if (len <= 0)
            return 0;
        ssize_t n = send(_sock, buf, len, flag);
        if (n < 0)
        {
            if (errno == EAGAIN || errno == EWOULDBLOCK || errno == EINTR)//非阻塞发送发送缓冲区已满 /  读取时被信号打断
            {
                return 0;
            }
            else
            {
                LOG(DEG, "send failed, %s", strerror(errno));
                return -1;
            }
        }
        return n;
    }

    void Close()
    {
        if (_sock != -1)
        {
            close(_sock);
            _sock = -1;
        }
    }

    bool createServer(uint16_t port) { return Create() && Bind(port) && Listen() && setReuseAddress(); }

    bool createClient(const std::string& ip, uint16_t port) { return Create() && Connect(ip, port); }

    bool setReuseAddress()//设置地址端口重用 当服务器主动断开连接后会进入TIME_WAIT状态 一段时间内可能无法使用该端口 设置重用后就不存在这个问题
    {
        int n = 1;
        if (setsockopt(_sock, SOL_SOCKET, SO_REUSEADDR, &n, sizeof (int)) == -1 || setsockopt(_sock, SOL_SOCKET, SO_REUSEPORT, &n, sizeof (int)) == -1)
        {
            LOG(DEG, "set reuse address failed");
            return false;
        }
        return true;
    }

    bool setNonBlock()//将socket设置为非阻塞
    {
        int opt = fcntl(_sock, F_GETFL, 0);
        if (fcntl(_sock, F_SETFL, opt | O_NONBLOCK) == -1)
        {
            LOG(DEG, "set nonblock failed");
            return false;
        }
        return true;
    }

};

#endif
